package com.wonder.delta;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.*;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Name: BaseCommands
 * @Description: java类作用描述
 * @Author: yn
 * @CreateDate: 2023/6/27 11:48
 * @Remark: 特殊说明
 * @Version: 1.0
 */
@ShellComponent
public class BaseCommands {

    private static final String TRANSFER = "TRANSFER";
    private static final String DEL = "DEL";

    /**
     *
     * extract-delta -base-path "C:\\Users\\yn\\Desktop\\wt文档" -target-path "C:\\Users\\yn\\Desktop\\dpm_运行环境\\wt文档" -delta-path "C:\\Users\\yn\\Desktop\\delta"
     * @param newPath
     * @param oldPath
     * @param deltaPath
     * @throws Exception
     */
    @ShellMethod(value = "输入地址，比较本次发布地址和上次发布地址不同并提取delta使上次发布地址与本次发布地址一致 （ -n:本次发布地址 -o:上次发布地址 -d:存放delta地址 ）",prefix = "-")
    public void extractDelta(@ShellOption(value = "-n",help = "本次发布地址") String newPath,@ShellOption(value = "-o",help = "上次发布地址") String oldPath,@ShellOption(value = "-d",help = "存放delta地址") String deltaPath) throws Exception {
        if(!FileUtil.exist(newPath)){
            throw new Exception("本次发布地址不能为空");
        }
        if(!FileUtil.exist(oldPath)){
            throw new Exception("上次发布地址不能为空");
        }
        FileUtil.clean(deltaPath);
        Collection<Command> deltaCommands = getDeltaCommand(newPath, oldPath);
        executeCommand(deltaCommands,newPath,deltaPath);
        JSONObject jsonByDeltaCommands = getJsonByDeltaCommands(deltaCommands);
        FileUtil.writeBytes(jsonByDeltaCommands.toStringPretty().getBytes(StandardCharsets.UTF_8),FileUtil.file(deltaPath,"DeltaCommands.json"));
    }

    @ShellMethod("输入地址，使用delta目录的文件补充上次发布地址（ -o:上次发布地址 -d:存放delta地址 ）")
    public void mergeByDelta(@ShellOption(value = "-o",help = "上次发布地址") String oldPath,@ShellOption(value = "-d",help = "存放delta地址") String deltaPath) throws Exception {
        if(!FileUtil.exist(oldPath)){
            throw new Exception("上次发布地址不能为空");
        }
        File file = FileUtil.file(deltaPath, "DeltaCommands.json");
        if(!file.exists()){
            throw new Exception("delta地址错误，没有变更列表");
        }
        String tempJson = FileUtil.readUtf8String(file);
        executeDeltaJson(tempJson,deltaPath,oldPath);
    }

    @ShellMethod(value = "输入地址，使上次发布地址与本次发布地址一致 （ -n:本次发布地址 -o:上次发布地址 ）",prefix = "-")
    public void merge(@ShellOption(value = "-n",help = "本次发布地址") String newPath,@ShellOption(value = "-o",help = "上次发布地址") String oldPath) throws Exception {
        if(!FileUtil.exist(newPath)){
            throw new Exception("本次发布地址不能为空");
        }
        if(!FileUtil.exist(oldPath)){
            throw new Exception("上次发布地址不能为空");
        }
        Collection<Command> deltaCommands = getDeltaCommand(newPath, oldPath);
        executeCommand(deltaCommands,newPath,oldPath);
    }

    private static JSONObject getJsonByDeltaCommands(Collection<Command> deltaCommands) {
        Map<String, List<String>> collect = deltaCommands.stream().distinct().collect(Collectors.groupingBy(Command::getOperation,Collectors.mapping(Command::getOperateFile,Collectors.toList())));
        return JSONUtil.parseObj(collect);
    }

    private static Collection<Command> getDeltaCommand(String newPath, String oldPath) {
        Map<String, String> baseFileHash = listFileHash(newPath);
        Map<String, String> targetFileHash = listFileHash(oldPath);

        List<Command> transferFiles = baseFileHash.keySet()
                .stream()
                .filter(s -> !targetFileHash.containsKey(s) || (targetFileHash.containsKey(s) && !Objects.equals(targetFileHash.get(s), baseFileHash.get(s))))
                .map(i -> Command.builder().operation(TRANSFER).operateFile(i).build())
                .collect(Collectors.toList());
        List<Command> delFiles = targetFileHash.keySet()
                .stream()
                .filter(s -> !baseFileHash.containsKey(s))
                .map(i -> Command.builder().operation(DEL).operateFile(i).build())
                .collect(Collectors.toList());
        return CollectionUtil.union(transferFiles,delFiles);
    }

    public static Map<String, String> listFileHash(String path) {
        if (!FileUtil.exist(path)) {
            return Collections.emptyMap();
        }
        Map<String, String> result = new HashMap();
        List<File> fileNames = FileUtil.loopFiles(path);
        fileNames.forEach(file -> {
            String relativePath = file.getAbsolutePath().substring(path.length());
            result.put(relativePath, calcMD5(file, relativePath));
        });
        return result;
    }

    /**
     * 计算文件 MD5
     *
     * @param file
     * @return 返回文件的md5字符串，如果计算过程中任务的状态变为取消或暂停，返回null， 如果有其他异常，返回空字符串
     */
    protected static String calcMD5(File file, String salt) {
        return MD5.create()
                .setSalt(salt.getBytes())
//				.setDigestCount(5)
                .digestHex(file);
    }


    private static void executeDeltaJson(String deltaJson, String newPath, String deltaPath) {
        JSONObject deltaJsonObject = JSONUtil.parseObj(deltaJson);
        for (String operation : deltaJsonObject.keySet()) {
            switch (operation.toUpperCase()){
                case TRANSFER:
                    List<String> transferFiles = deltaJsonObject.getBeanList(TRANSFER, String.class);
                    transferFiles.forEach(item -> {
                        File dest = FileUtil.file(deltaPath, item);
                        File src = FileUtil.file(newPath, item);
                        FileUtil.copyContent(src, dest, true);
                        System.out.println("复制："+src.getAbsolutePath()+" -> "+dest.getAbsolutePath());
                    });
                    break;
                case DEL:
                    List<String> delFiles = deltaJsonObject.getBeanList(DEL, String.class);
                    delFiles.forEach(item -> {
                        File file = FileUtil.file(deltaPath, item);
                        FileUtil.del(file);
                        System.out.println("删除："+file.getAbsolutePath());
                    });
                    break;
                default:
            }
        }
    }

    private static void executeCommand(Collection<Command> deltaCommands, String newPath, String deltaPath) {
        deltaCommands.forEach(item->{
            executeCommand(item,newPath,deltaPath);
        });
    }

    private static void executeCommand(Command item, String newPath, String deltaPath) {
        File file = FileUtil.file(deltaPath, item.getOperateFile());
        switch (item.getOperation().toUpperCase()){
            case TRANSFER:
                File src = FileUtil.file(newPath, item.getOperateFile());
                FileUtil.copyContent(src, file, true);
                System.out.println("复制："+src.getAbsolutePath()+" -> "+file.getAbsolutePath());
                break;
            case DEL:
                FileUtil.del(file);
                System.out.println("删除："+file.getAbsolutePath());
                break;
            default:
        }
    }

}


